msg_tool\scripts\emote/
pimg.rs

1//! Emote Multiple Image File (.pimg)
2use crate::ext::io::*;
3use crate::ext::psb::*;
4use crate::scripts::base::*;
5use crate::types::*;
6use crate::utils::img::*;
7use crate::utils::psd::*;
8use crate::utils::struct_pack::*;
9use anyhow::Result;
10use emote_psb::PsbReader;
11use libtlg_rs::*;
12use std::collections::HashMap;
13use std::io::{Read, Seek};
14use std::path::Path;
15use std::sync::OnceLock;
16
17/// Cached overlay state for PImg composite image computation.
18#[derive(Debug)]
19struct PImgOverlayState {
20    width: u32,
21    height: u32,
22    bases: HashMap<i64, (Tlg, u32, u32, u8)>,
23    base_id: i64,
24    all_non_diff: bool,
25}
26
27#[derive(Debug)]
28/// Emote PImg Script Builder
29pub struct PImgBuilder {}
30
31impl PImgBuilder {
32    /// Creates a new instance of `PImgBuilder`
33    pub const fn new() -> Self {
34        Self {}
35    }
36}
37
38impl ScriptBuilder for PImgBuilder {
39    fn default_encoding(&self) -> Encoding {
40        Encoding::Utf8
41    }
42
43    fn build_script(
44        &self,
45        buf: Vec<u8>,
46        filename: &str,
47        _encoding: Encoding,
48        _archive_encoding: Encoding,
49        config: &ExtraConfig,
50        _archive: Option<&Box<dyn Script>>,
51    ) -> Result<Box<dyn Script + Send + Sync>> {
52        Ok(Box::new(PImg::new(MemReader::new(buf), filename, config)?))
53    }
54
55    fn build_script_from_file(
56        &self,
57        filename: &str,
58        _encoding: Encoding,
59        _archive_encoding: Encoding,
60        config: &ExtraConfig,
61        _archive: Option<&Box<dyn Script>>,
62    ) -> Result<Box<dyn Script + Send + Sync>> {
63        if filename == "-" {
64            let data = crate::utils::files::read_file(filename)?;
65            Ok(Box::new(PImg::new(MemReader::new(data), filename, config)?))
66        } else {
67            let f = std::fs::File::open(filename)?;
68            let reader = std::io::BufReader::new(f);
69            Ok(Box::new(PImg::new(reader, filename, config)?))
70        }
71    }
72
73    fn build_script_from_reader<'a>(
74        &self,
75        reader: Box<dyn ReadSeek + Send + Sync + 'a>,
76        filename: &str,
77        _encoding: Encoding,
78        _archive_encoding: Encoding,
79        config: &ExtraConfig,
80        _archive: Option<&Box<dyn Script>>,
81    ) -> Result<Box<dyn Script + Send + Sync + 'a>> {
82        Ok(Box::new(PImg::new(reader, filename, config)?))
83    }
84
85    fn extensions(&self) -> &'static [&'static str] {
86        &["pimg"]
87    }
88
89    fn script_type(&self) -> &'static ScriptType {
90        &ScriptType::EmotePimg
91    }
92
93    fn is_this_format(&self, filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
94        if Path::new(filename)
95            .extension()
96            .map(|ext| ext.to_ascii_lowercase() == "pimg")
97            .unwrap_or(false)
98            && buf_len >= 4
99            && buf.starts_with(b"PSB\0")
100        {
101            return Some(255);
102        }
103        None
104    }
105
106    fn is_image(&self) -> bool {
107        true
108    }
109}
110
111struct PImgLayer<'a> {
112    data: &'a PsbValueFixed,
113    name: &'a str,
114    layer_id: i64,
115    /// seems is layer type in PSD files
116    layer_type: i64,
117    left: u32,
118    top: u32,
119    width: u32,
120    height: u32,
121    opacity: u8,
122    visible: bool,
123    type_: i64,
124    children: Vec<PImgLayer<'a>>,
125}
126
127impl std::fmt::Debug for PImgLayer<'_> {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        f.debug_struct("PImgLayer")
130            .field("layer_id", &self.layer_id)
131            .field("layer_type", &self.layer_type)
132            .field("name", &self.name)
133            .field("left", &self.left)
134            .field("top", &self.top)
135            .field("width", &self.width)
136            .field("height", &self.height)
137            .field("opacity", &self.opacity)
138            .field("visible", &self.visible)
139            .field("type", &self.type_)
140            .field("children", &self.children)
141            .finish()
142    }
143}
144
145impl<'a> PImgLayer<'a> {
146    pub fn new(data: &'a PsbValueFixed, layers: &'a PsbValueFixed) -> Result<Self> {
147        let layer_id = data["layer_id"]
148            .as_i64()
149            .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid layer_id"))?;
150        let layer_type = data["layer_type"]
151            .as_i64()
152            .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid layer_type"))?;
153        let name = data["name"]
154            .as_str()
155            .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid name"))?;
156        let left = data["left"].as_u32();
157        let top = data["top"].as_u32();
158        let width = data["width"].as_u32();
159        let height = data["height"].as_u32();
160        let (left, top, width, height) = if layer_type != 0 {
161            (
162                left.unwrap_or(0),
163                top.unwrap_or(0),
164                width.unwrap_or(0),
165                height.unwrap_or(0),
166            )
167        } else {
168            (
169                left.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid left"))?,
170                top.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid top"))?,
171                width.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid width"))?,
172                height.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid height"))?,
173            )
174        };
175        let opacity = data["opacity"]
176            .as_u8()
177            .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid opacity"))?;
178        let visible = data["visible"].as_i64().unwrap_or(1) != 0;
179        let type_ = data["type"]
180            .as_i64()
181            .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid type"))?;
182        let mut children = Vec::new();
183        for layer in layers.members() {
184            if layer_type == 2 || layer_type == 1 {
185                if let Some(parent_id) = layer["group_layer_id"].as_i64() {
186                    if parent_id == layer_id {
187                        children.push(PImgLayer::new(layer, layers)?);
188                    }
189                }
190            } else if layer_type == 0 {
191                if let Some(base_id) = layer["diff_id"].as_i64() {
192                    if base_id == layer_id {
193                        children.push(PImgLayer::new(layer, layers)?);
194                    }
195                }
196            }
197        }
198        Ok(Self {
199            data,
200            layer_id,
201            layer_type,
202            name,
203            left,
204            top,
205            width,
206            height,
207            opacity,
208            visible,
209            type_,
210            children,
211        })
212    }
213
214    fn len(&self) -> usize {
215        1 + self.children.iter().map(|c| c.len()).sum::<usize>()
216    }
217
218    fn load_img(&self, img: &PImg) -> Result<ImageData> {
219        if self.layer_type == 2 || self.layer_type == 1 {
220            anyhow::bail!("Group layers do not have image data");
221        }
222        if self.layer_id == -1 {
223            // Generate a empty image
224            Ok(ImageData {
225                width: self.width,
226                height: self.height,
227                color_type: ImageColorType::Rgba,
228                depth: 8,
229                data: vec![0u8; (self.width * self.height * 4) as usize],
230            })
231        } else {
232            let tlg = img.load_img(self.layer_id).map_err(|e| {
233                anyhow::anyhow!("Failed to load image for layer_id {}: {}", self.layer_id, e)
234            })?;
235            let mut img = ImageData {
236                width: tlg.width,
237                height: tlg.height,
238                color_type: match tlg.color {
239                    TlgColorType::Bgr24 => ImageColorType::Bgr,
240                    TlgColorType::Bgra32 => ImageColorType::Bgra,
241                    TlgColorType::Grayscale8 => ImageColorType::Grayscale,
242                },
243                depth: 8,
244                data: tlg.data.clone(),
245            };
246            convert_to_rgba(&mut img)?;
247            Ok(img)
248        }
249    }
250
251    fn save_to_psd(&self, img: &PImg, psd: &mut PsdWriter, base: &mut ImageData) -> Result<()> {
252        if self.children.is_empty() {
253            let img = self.load_img(img)?;
254            let mut visible = self.visible;
255            if !self.data["diff_id"].is_none() {
256                visible = false; // Diff layers are always hide by default
257            }
258            if visible {
259                draw_on_img_with_opacity(base, &img, self.left, self.top, self.opacity)?;
260            }
261            let layer_name_source_setting = LayerNameSourceSetting {
262                id: self.layer_id as i32,
263            };
264            let mut packed = Vec::new();
265            layer_name_source_setting.pack(&mut packed, true, Encoding::Utf8, &None)?;
266            let additional_info = vec![AdditionalLayerInfo {
267                signature: *IMAGE_RESOURCE_SIGNATURE,
268                key: *LAYER_NAME_SOURCE_SETTING_KEY,
269                data: packed,
270            }];
271            let option = PsdLayerOption {
272                visible,
273                opacity: self.opacity,
274                additional_info,
275            };
276            psd.add_layer(self.name, self.left, self.top, img, Some(option))?;
277        } else {
278            psd.add_layer_group_end()?;
279            if self.layer_type == 0 {
280                let img = self.load_img(img)?;
281                let visible = self.visible;
282                if visible {
283                    draw_on_img_with_opacity(base, &img, self.left, self.top, self.opacity)?;
284                }
285                let layer_name_source_setting = LayerNameSourceSetting {
286                    id: self.layer_id as i32,
287                };
288                let mut packed = Vec::new();
289                layer_name_source_setting.pack(&mut packed, true, Encoding::Utf8, &None)?;
290                let additional_info = vec![AdditionalLayerInfo {
291                    signature: *IMAGE_RESOURCE_SIGNATURE,
292                    key: *LAYER_NAME_SOURCE_SETTING_KEY,
293                    data: packed,
294                }];
295                let option = PsdLayerOption {
296                    visible,
297                    opacity: self.opacity,
298                    additional_info,
299                };
300                psd.add_layer(self.name, self.left, self.top, img, Some(option))?;
301            }
302            for child in &self.children {
303                child.save_to_psd(img, psd, base)?;
304            }
305            let layer_name_source_setting = LayerNameSourceSetting {
306                id: self.layer_id as i32,
307            };
308            let mut packed = Vec::new();
309            layer_name_source_setting.pack(&mut packed, true, Encoding::Utf8, &None)?;
310            let additional_info = vec![AdditionalLayerInfo {
311                signature: *IMAGE_RESOURCE_SIGNATURE,
312                key: *LAYER_NAME_SOURCE_SETTING_KEY,
313                data: packed,
314            }];
315            let option = if self.layer_type == 0 {
316                Some(PsdLayerOption {
317                    additional_info,
318                    ..Default::default()
319                })
320            } else {
321                Some(PsdLayerOption {
322                    visible: self.visible,
323                    opacity: self.opacity,
324                    additional_info,
325                })
326            };
327            psd.add_layer_group(self.name, self.layer_type == 2, option)?;
328        }
329        Ok(())
330    }
331}
332
333#[derive(Debug)]
334struct PImgLayerRoot<'a> {
335    layers: Vec<PImgLayer<'a>>,
336}
337
338impl<'a> PImgLayerRoot<'a> {
339    pub fn new(layers: &'a PsbValueFixed) -> Result<Self> {
340        let mut root_layers = Vec::new();
341        for layer in layers.members() {
342            if layer["group_layer_id"].is_none() && layer["diff_id"].is_none() {
343                root_layers.push(PImgLayer::new(layer, layers)?);
344            }
345        }
346        Ok(Self {
347            layers: root_layers,
348        })
349    }
350
351    fn len(&self) -> usize {
352        self.layers.iter().map(|l| l.len()).sum()
353    }
354
355    fn save_to_psd(&self, img: &PImg, psd: &mut PsdWriter, base: &mut ImageData) -> Result<()> {
356        for layer in &self.layers {
357            layer.save_to_psd(img, psd, base)?;
358        }
359        Ok(())
360    }
361}
362
363#[derive(Debug)]
364/// Emote PImg Script
365pub struct PImg {
366    psb: VirtualPsbFixed,
367    overlay: Option<bool>,
368    psd: bool,
369    psd_compress: bool,
370    zlib_compression_level: u32,
371    psd_no_diff: bool,
372    overlay_state: OnceLock<PImgOverlayState>,
373}
374
375impl PImg {
376    /// Create a new PImg script
377    ///
378    /// * `reader` - The reader containing the PImg script data
379    /// * `filename` - The name of the file
380    /// * `config` - Extra configuration options
381    pub fn new<R: Read + Seek>(reader: R, _filename: &str, config: &ExtraConfig) -> Result<Self> {
382        let psb = PsbReader::open_psb_v2(reader)?.to_psb_fixed();
383        Ok(Self {
384            psb,
385            overlay: config.emote_pimg_overlay,
386            psd: config.emote_pimg_psd,
387            psd_compress: config.psd_compress,
388            zlib_compression_level: config.zlib_compression_level,
389            psd_no_diff: config.emote_pimg_psd_no_diff,
390            overlay_state: OnceLock::new(),
391        })
392    }
393
394    fn load_img(&self, layer_id: i64) -> Result<Tlg> {
395        let layer_id = layer_id as usize;
396        let psb = self.psb.root();
397        let reference = &psb[format!("{layer_id}.tlg")];
398        let resource_id = reference
399            .resource_id()
400            .ok_or_else(|| anyhow::anyhow!("Layer {layer_id} does not have a resource ID"))?
401            as usize;
402        if resource_id >= self.psb.resources().len() {
403            return Err(anyhow::anyhow!(
404                "Resource ID {resource_id} for layer {layer_id} is out of bounds"
405            ));
406        }
407        let resource = &self.psb.resources()[resource_id];
408        Ok(load_tlg(MemReaderRef::new(&resource))?)
409    }
410
411    /// Returns true if overlay composite mode should be used.
412    fn overlay_mode(&self) -> bool {
413        let psb = self.psb.root();
414        self.overlay.unwrap_or_else(|| {
415            psb["layers"]
416                .members()
417                .all(|layer| layer["group_layer_id"].is_none())
418        })
419    }
420
421    /// Computes the overlay state by scanning all layers and loading base images.
422    fn compute_overlay_state(&self) -> Result<PImgOverlayState> {
423        let psb = self.psb.root();
424        let width = psb["width"]
425            .as_u32()
426            .ok_or(anyhow::anyhow!("missing width"))?;
427        let height = psb["height"]
428            .as_u32()
429            .ok_or(anyhow::anyhow!("missing height"))?;
430        let is_all_non_diff = psb["layers"]
431            .members()
432            .all(|layer| layer["diff_id"].is_none());
433        let mut bases = HashMap::new();
434        let mut base_id = None;
435        for i in psb["layers"].members() {
436            if !i["diff_id"].is_none() {
437                continue;
438            }
439            let layer_id = i["layer_id"]
440                .as_i64()
441                .ok_or(anyhow::anyhow!("missing layer_id"))?;
442            let top = i["top"].as_u32().ok_or(anyhow::anyhow!("missing top"))?;
443            let left = i["left"].as_u32().ok_or(anyhow::anyhow!("missing left"))?;
444            if is_all_non_diff {
445                let w = i["width"]
446                    .as_u32()
447                    .ok_or(anyhow::anyhow!("missing width for non-diff layer"))?;
448                let h = i["height"]
449                    .as_u32()
450                    .ok_or(anyhow::anyhow!("missing height for non-diff layer"))?;
451                if !(top == 0 && left == 0 && w == width && h == height) {
452                    continue;
453                }
454            }
455            let opacity = i["opacity"]
456                .as_u8()
457                .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid opacity"))?;
458            bases.insert(layer_id, (self.load_img(layer_id)?, top, left, opacity));
459            base_id = Some(layer_id);
460        }
461        Ok(PImgOverlayState {
462            width,
463            height,
464            bases,
465            base_id: base_id.ok_or(anyhow::anyhow!("No valid base layer found"))?,
466            all_non_diff: is_all_non_diff,
467        })
468    }
469
470    /// Returns the cached overlay state, computing it once if needed.
471    fn get_overlay_state(&self) -> Result<&PImgOverlayState> {
472        if let Some(state) = self.overlay_state.get() {
473            return Ok(state);
474        }
475        let state = self.compute_overlay_state()?;
476        Ok(self.overlay_state.get_or_init(|| state))
477    }
478}
479
480impl Script for PImg {
481    fn default_output_script_type(&self) -> OutputScriptType {
482        OutputScriptType::Custom
483    }
484
485    fn is_output_supported(&self, output: OutputScriptType) -> bool {
486        matches!(output, OutputScriptType::Custom)
487    }
488
489    fn custom_output_extension<'a>(&'a self) -> &'a str {
490        "psd"
491    }
492
493    fn default_format_type(&self) -> FormatOptions {
494        FormatOptions::None
495    }
496
497    fn is_image(&self) -> bool {
498        !self.psd
499    }
500
501    fn is_multi_image(&self) -> bool {
502        !self.psd
503    }
504
505    fn iter_multi_image_name<'a>(
506        &'a self,
507    ) -> Result<Box<dyn Iterator<Item = Result<String>> + 'a>> {
508        let psb = self.psb.root();
509        if !self.overlay_mode() {
510            Ok(Box::new(
511                psb.iter()
512                    .filter(|(k, _)| k.ends_with(".tlg"))
513                    .map(|(k, _)| Ok(k.trim_end_matches(".tlg").to_string())),
514            ))
515        } else {
516            if !psb["layers"].is_list() {
517                return Err(anyhow::anyhow!("layers is not a list"));
518            }
519            Ok(Box::new(psb["layers"].members().map(|layer| {
520                layer["name"]
521                    .as_str()
522                    .map(|s| s.to_string())
523                    .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid name"))
524            })))
525        }
526    }
527
528    fn open_image<'a>(&'a self, index: usize) -> Result<ImageDataWithName> {
529        let psb = self.psb.root();
530        if !self.overlay_mode() {
531            // Non-overlay mode: find the index-th .tlg entry and load it directly
532            let mut count = 0;
533            for (k, v) in psb.iter() {
534                if !k.ends_with(".tlg") {
535                    continue;
536                }
537                if count == index {
538                    let resource_id = v
539                        .resource_id()
540                        .ok_or_else(|| anyhow::anyhow!("Layer {} does not have a resource ID", k))?
541                        as usize;
542                    let name = k.trim_end_matches(".tlg").to_string();
543                    if resource_id >= self.psb.resources().len() {
544                        return Err(anyhow::anyhow!(
545                            "Resource ID {} for layer {} is out of bounds",
546                            resource_id,
547                            k
548                        ));
549                    }
550                    let resource = &self.psb.resources()[resource_id];
551                    let tlg = load_tlg(MemReaderRef::new(resource))?;
552                    return Ok(ImageDataWithName {
553                        name,
554                        data: ImageData {
555                            width: tlg.width,
556                            height: tlg.height,
557                            color_type: match tlg.color {
558                                TlgColorType::Bgr24 => ImageColorType::Bgr,
559                                TlgColorType::Bgra32 => ImageColorType::Bgra,
560                                TlgColorType::Grayscale8 => ImageColorType::Grayscale,
561                            },
562                            depth: 8,
563                            data: tlg.data.clone(),
564                        },
565                    });
566                }
567                count += 1;
568            }
569            Err(anyhow::anyhow!("Layer index {} out of bounds", index))
570        } else {
571            // Overlay mode: composite the index-th layer
572            let state = self.get_overlay_state()?;
573            let layer = psb["layers"]
574                .members()
575                .nth(index)
576                .ok_or_else(|| anyhow::anyhow!("Layer index {} out of bounds", index))?;
577            let layer_id = layer["layer_id"]
578                .as_i64()
579                .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid layer_id"))?;
580            let layer_name = layer["name"]
581                .as_str()
582                .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid name"))?;
583            let width = layer["width"]
584                .as_u32()
585                .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid width"))?;
586            let height = layer["height"]
587                .as_u32()
588                .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid height"))?;
589            let top = layer["top"]
590                .as_u32()
591                .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid top"))?;
592            let left = layer["left"]
593                .as_u32()
594                .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid left"))?;
595            let opacity = layer["opacity"]
596                .as_u8()
597                .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid opacity"))?;
598            if (layer["diff_id"].is_none() && !state.all_non_diff)
599                || (state.all_non_diff && layer_id == state.base_id)
600            {
601                // Base layer (no diff compositing needed)
602                let base = &state
603                    .bases
604                    .get(&layer_id)
605                    .ok_or_else(|| {
606                        anyhow::anyhow!("Base image for layer_id {} not found", layer_id)
607                    })?
608                    .0;
609                let mut data = ImageData {
610                    width: state.width,
611                    height: state.height,
612                    color_type: match base.color {
613                        TlgColorType::Bgr24 => ImageColorType::Bgr,
614                        TlgColorType::Bgra32 => ImageColorType::Bgra,
615                        TlgColorType::Grayscale8 => ImageColorType::Grayscale,
616                    },
617                    depth: 8,
618                    data: base.data.clone(),
619                };
620                if opacity != 255 {
621                    apply_opacity(&mut data, opacity)?;
622                }
623                if state.width != width || state.height != height || top != 0 || left != 0 {
624                    data = draw_on_canvas(data, state.width, state.height, left, top)?;
625                }
626                Ok(ImageDataWithName {
627                    name: layer_name.to_string(),
628                    data,
629                })
630            } else {
631                // Diff layer: composite diff onto base
632                let diff_id = if state.all_non_diff {
633                    state.base_id
634                } else {
635                    layer["diff_id"]
636                        .as_i64()
637                        .ok_or_else(|| anyhow::anyhow!("Layer does not have a valid diff_id"))?
638                };
639                let (base, base_top, base_left, base_opacity) = state
640                    .bases
641                    .get(&diff_id)
642                    .ok_or_else(|| anyhow::anyhow!("Base image layer {} not found", diff_id))?;
643                let diff = self.load_img(layer_id)?;
644                if base.color != diff.color {
645                    return Err(anyhow::anyhow!(
646                        "Color type mismatch for layer_id {}: base color {:?}, diff color {:?}",
647                        layer_id,
648                        base.color,
649                        diff.color
650                    ));
651                }
652                let mut base_img = ImageData {
653                    width: base.width,
654                    height: base.height,
655                    color_type: match base.color {
656                        TlgColorType::Bgr24 => ImageColorType::Bgr,
657                        TlgColorType::Bgra32 => ImageColorType::Bgra,
658                        TlgColorType::Grayscale8 => ImageColorType::Grayscale,
659                    },
660                    depth: 8,
661                    data: base.data.clone(),
662                };
663                if base.width != state.width
664                    || base.height != state.height
665                    || *base_top != 0
666                    || *base_left != 0
667                {
668                    base_img =
669                        draw_on_canvas(base_img, state.width, state.height, *base_left, *base_top)?;
670                }
671                if *base_opacity != 255 {
672                    apply_opacity(&mut base_img, *base_opacity)?;
673                }
674                let diff_img = ImageData {
675                    width: diff.width,
676                    height: diff.height,
677                    color_type: match diff.color {
678                        TlgColorType::Bgr24 => ImageColorType::Bgr,
679                        TlgColorType::Bgra32 => ImageColorType::Bgra,
680                        TlgColorType::Grayscale8 => ImageColorType::Grayscale,
681                    },
682                    depth: 8,
683                    data: diff.data.clone(),
684                };
685                draw_on_img_with_opacity(&mut base_img, &diff_img, left, top, opacity)?;
686                Ok(ImageDataWithName {
687                    name: layer_name.to_string(),
688                    data: base_img,
689                })
690            }
691        }
692    }
693
694    fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
695        let psb = self.psb.root();
696        let width = psb["width"]
697            .as_u32()
698            .ok_or(anyhow::anyhow!("missing width"))?;
699        let height = psb["height"]
700            .as_u32()
701            .ok_or(anyhow::anyhow!("missing height"))?;
702        let mut psd = PsdWriter::new(width, height, ImageColorType::Rgba, 8, encoding)?
703            .compress(self.psd_compress)
704            .zlib_compression_level(self.zlib_compression_level);
705        let mut base = ImageData {
706            width,
707            height,
708            color_type: ImageColorType::Rgba,
709            depth: 8,
710            data: vec![0u8; (width * height * 4) as usize],
711        };
712        if !self.psd_no_diff {
713            let is_no_group = psb["layers"]
714                .members()
715                .all(|layer| layer["group_layer_id"].is_none());
716            let is_all_non_diff = psb["layers"]
717                .members()
718                .all(|layer| layer["diff_id"].is_none());
719            if is_all_non_diff && is_no_group {
720                let mut psb = psb.clone();
721                let diff_id = {
722                    let base_layer = psb["layers"]
723                        .members()
724                        .rev()
725                        .find(|layer| {
726                            layer["diff_id"].is_none()
727                                && layer["top"].as_i64() == Some(0)
728                                && layer["left"].as_i64() == Some(0)
729                                && layer["width"].as_u32() == Some(width)
730                                && layer["height"].as_u32() == Some(height)
731                        })
732                        .ok_or(anyhow::anyhow!(
733                            "No valid base layer found for overlay mode"
734                        ))?;
735                    base_layer["layer_id"]
736                        .as_i64()
737                        .ok_or(anyhow::anyhow!("missing layer_id for base layer"))?
738                };
739                for layer in psb["layers"].members_mut() {
740                    let layer_id = layer["layer_id"].as_i64().unwrap_or(-1);
741                    if layer_id != diff_id {
742                        layer["diff_id"] = diff_id.into();
743                    }
744                }
745                let layers = PImgLayerRoot::new(&psb["layers"])?;
746                if layers.len() != psb["layers"].len() {
747                    return Err(anyhow::anyhow!("Layer hierarchy is invalid"));
748                }
749                layers.save_to_psd(self, &mut psd, &mut base)?;
750                let file = std::fs::File::create(filename)?;
751                let mut writer = std::io::BufWriter::new(file);
752                psd.save(base, &mut writer)?;
753                return Ok(());
754            }
755        }
756        let layers = PImgLayerRoot::new(&psb["layers"])?;
757        if layers.len() != psb["layers"].len() {
758            return Err(anyhow::anyhow!("Layer hierarchy is invalid"));
759        }
760        layers.save_to_psd(self, &mut psd, &mut base)?;
761        let file = std::fs::File::create(filename)?;
762        let mut writer = std::io::BufWriter::new(file);
763        psd.save(base, &mut writer)?;
764        Ok(())
765    }
766}